import java.io.*;
import java.net.*;
import java.util.StringTokenizer;

public class ftp_client
{

	static ftp_client forFunc = new ftp_client();			//for calling function only =.=
	static BufferedReader inFromServer;
	static BufferedWriter outToServer;
	static Socket clientSocket = null;

	static String user = "anonymous";
	static String passwd = "anonymous";

   public static void main(String argv[]) throws Exception
   {
       

	   //getting client's command--------------------
	   String c_command = "";
	   String client_command;
	   String para[];
	   
	   while(!c_command.equals("quit"))
	   {
		   BufferedReader client_input = new BufferedReader( new InputStreamReader(System.in));
		   System.out.print("ftp>");
	   
		   client_command = client_input.readLine();
		   StringTokenizer token = new StringTokenizer(client_command);
		   c_command = token.nextToken();
		   para = new String[2];
		   para[0] = null;
		   para[1] = null;
		   int i =0;
		   
		   while (token.hasMoreTokens())
		   {
			   para[i] = token.nextToken();
			   ++i;
		   }

		   if (c_command.equals("connect"))
		   {
			   if (clientSocket != null)
				   System.out.println("You have logged in");
			   else
			   {
				   if (para[0] != null)
					   forFunc.connect_server(para[0]);
				   else
					   System.out.println("Parameter is missing!");
			   }
		   }
		   else if (c_command.equals("user"))
		   {
			   if (clientSocket == null && para[0] != null && para[1] != null)
			   {
				   user = para[0];
				   passwd = para[1];
			   }
		   }


		  //Can be call only if connected----------------
		   ////////////////////////////////////////////////////////////////////////
		   else if (c_command.equals("list"))
		   {
			   if (clientSocket != null)
				   forFunc.list(para[0]);
			   else
				   System.out.println("Please connect first");
		   }

		   else if (c_command.equals("get"))
		   {
			   if (clientSocket != null)
			   {
				   if (para[0] != null && para[1] != null)
				   {
					   forFunc.downloadFile(para[0], para[1]);
				   }
				   else
					   System.out.println("Parameter(s) is(are) missing");
			   }
			   else
				   System.out.println("Please connect first");
		   }
		   else if (c_command.equals("getdir"))
		   {
			   if (clientSocket != null)
			   {
				   if (para[0] != null && para[1] != null)
				   {
					   //sendLine("PWD");
					   //String temp = readLine();
					   //sendLine("CWD " + para[0]);
					   //System.out.println(readLine());
					   forFunc.get_dir(para[0], para[1]);
					   //sendLine("CWD " + "/");

				   }
				   else
					   System.out.println("Parameter(s) is(are) missing");
			   }
			   else
				   System.out.println("Please connect first");
		   }
		   else if (c_command.equals("put"))
		   {
			   if (clientSocket != null)
			   {
				   if (para[0] != null && para[1] != null)
					   forFunc.uploadFile(para[0], para[1]);
				   else
					   System.out.println("Parameter(s) is(are) missing");
			   }
			   else
				   System.out.println("Please connect first");
		   }
		   else if (c_command.equals("pwd"))
		   {
			   if (clientSocket != null)
			   {
				   sendLine("PWD");
				   String temp = readLine();
				   System.out.println(temp);
			   }
			   else
				   System.out.println("Please connect first");
		   }
		   else if (c_command.equals("cd"))
		   {
			   if (clientSocket != null)
			   {
				   if (para[0] != null)
				   {
					   sendLine("CWD " + para[0]);
					   System.out.println(readLine());
				   }
				   else
					   System.out.println("Parameter is missing");
			   }
			   else
				   System.out.println("Please connect first");
		   }
		   else if (c_command.equals("mkdir"))
		   {
			   if (clientSocket != null)
			   {
				   if (para[0] != null)
				   {
					   sendLine("MKD " + para[0]);
					   System.out.println("MKD " + para[0]);
					   System.out.println(readLine());
				   }
				   else
					   System.out.println("Parameter is missing");
			   }
			   else
				   System.out.println("Please connect first");
		   }
		   else if (c_command.equals("rmdir"))
		   {
			   if (clientSocket != null)
			   {
				   if (para[0] != null)
				   {
					   sendLine("RMD " + para[0]);
					   System.out.println("RMD " + para[0]);
					   System.out.println(readLine());
				   }
				   else
					   System.out.println("Parameter is missing");
			   }
			   else
				   System.out.println("Please connect first");
		   }
		   //////////////////////////////////////////////////////////////////////////

		   else if (c_command.equals("quit"))
			   break;
		   else
			   System.out.println("Command not found");
		   
	   }
	 
	   

	   //--------------------------------
	   sendLine("QUIT");
	   if (clientSocket != null)
		   clientSocket.close();
   }


	public boolean connect_server(String urlWebPage)
	{
		try
		{
			StringTokenizer tokenizer = new StringTokenizer(urlWebPage, ":");
			String host = tokenizer.nextToken();
			int port = 21;
			if (tokenizer.countTokens() > 0)
				port = Integer.parseInt(tokenizer.nextToken());

			clientSocket = new Socket(host, port);                 // connect to the web server;


			inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
			outToServer = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));

			printServerMsg();
			//------------------------- interface --------------

			sendLine("USER " + user);
			printServerMsg();

			sendLine("PASS " + passwd);
			printServerMsg();

			return true;
		}

		catch (Exception e)
		{
			System.out.println(e);
		}

		return true;
	}
   
   //--------------------------------------------
   
   public class Address {
		Address() {
			Port = -1;
		}
		public String IP;
		public int Port;
	};


	public Address  PASV () throws IOException{
		Address result = new Address();
		
		sendLine("PASV");
		
		String retMsg = readLine();
		if (!retMsg.startsWith("227 "))
			return result;
		
		int opening = retMsg.indexOf('(');
		int closing = retMsg.indexOf(')', opening + 1);

		if (closing > 0)
		{
			String dataLink = retMsg.substring(opening + 1, closing);
			StringTokenizer tokenizer = new StringTokenizer(dataLink, ",");

			String ip = tokenizer.nextToken() + "." + tokenizer.nextToken() + "." + tokenizer.nextToken() + "." + tokenizer.nextToken();

			int port = Integer.parseInt(tokenizer.nextToken()) * 256 + Integer.parseInt(tokenizer.nextToken());

			result.IP = ip;
			result.Port = port;
		}

		return result;
	}
	
	public boolean get_dir(String s_dir, String d_dir) throws IOException
	{
		Address newPort = forFunc.PASV();				//enter passive mode
		
		if (newPort.Port == -1)
			return false;
		
		Socket dataSocket = new Socket(newPort.IP, newPort.Port);
		
		//-Client create and enter a new directory ------------------------------------
		File curDir = new File(new File(".").getCanonicalPath());
		
		File file;
		if (d_dir.startsWith("/"))
		{
			file = new File(new File(".").getCanonicalFile(), d_dir);
			file.mkdirs();
			System.out.println(file.getPath());
		}
		else
		{
			file = new File(curDir, d_dir);
			file.mkdirs(); 
			System.out.println(file.getPath());
		}
		
		//-Get file names in the server-------------------------------------------------

		sendLine("NLST "+ s_dir);
		
		String ServerMsg = readLine();
		if (!ServerMsg.startsWith("150 "))
			return false;
		System.out.println(ServerMsg);
		
		DataInputStream getData = new DataInputStream(dataSocket.getInputStream());
		DataOutputStream printList = new DataOutputStream(System.out);
		
		int recv = 0;
		int bufferSize = 1024;
		byte[] buffer = new byte[bufferSize];
		
		ServerMsg = readLine();
		if (!ServerMsg.startsWith("226 "))
			return false;
		System.out.println(ServerMsg);
		
		//-For each of the file in the server, download the file------------------------------
		BufferedReader test = new BufferedReader(new InputStreamReader(dataSocket.getInputStream()));
		String file_name;
				
		while ((file_name = test.readLine()) !=null) {
			System.out.println("get " + s_dir+"/"+ file_name);
			forFunc.downloadFile(s_dir+"/"+file_name, d_dir+"/"+file_name);
			//forFunc.downloadFile(s_dir + "/" + "342 HW4 Q1b.xls", d_dir + "/" + "342 HW4 Q1b.xls");
		}
		
		//-Client return to home directory---------------------------------------------

		getData.close();
		dataSocket.close();
		
		return true;
	}
	
	public boolean list(String dir) throws IOException
	{

		Address newPort = forFunc.PASV();				//enter passive mode

		if (newPort.Port == -1)
			return false;

		Socket dataSocket = new Socket(newPort.IP, newPort.Port);	//connect to the data socket of server;

		//System.out.println(dir);
		if (dir != null)
		{
			sendLine("LIST " + dir);
		}
		else
			sendLine("LIST");

		String ServerMsg = readLine();
		if (!ServerMsg.startsWith("150 "))
			return false;
		System.out.println(ServerMsg);

		//-------------- get list -----------------
		DataInputStream getData = new DataInputStream(dataSocket.getInputStream());
		DataOutputStream printList = new DataOutputStream(System.out);
		int recv = 0;
		int bufferSize = 1024;
		byte[] buffer = new byte[bufferSize];

		ServerMsg = readLine();
		System.out.println(ServerMsg);
		if (!ServerMsg.startsWith("226 "))
		{
			getData.close();
			dataSocket.close();
			return false;
		}

		while ((recv = getData.read(buffer, 0, bufferSize)) != -1)
		{
			printList.write(buffer,0,recv);
		}
		getData.close();
		dataSocket.close();
		
		return true;
	}
	
	// get src_filename form server and rename the file as dst_filename
	public boolean downloadFile(String src_filename, String dst_filename) throws IOException
	{
		File d_file;
		if (dst_filename.startsWith("/"))
			d_file = new File(new File(".").getCanonicalFile(), dst_filename);
		else
			d_file = new File(dst_filename);


		Address newPort = forFunc.PASV();				//enter passive mode

		if (newPort.Port == -1)
			return false;

		Socket dataSocket = new Socket(newPort.IP, newPort.Port);

		sendLine("RETR " + src_filename);

		String ServerMsg = readLine();
		if (!ServerMsg.startsWith("150 "))
			return false;
		System.out.println(ServerMsg);

		DataInputStream getData = new DataInputStream(dataSocket.getInputStream());
		DataOutputStream printData = new DataOutputStream(dataSocket.getOutputStream());

		DataOutputStream IntoFile = new DataOutputStream(new FileOutputStream(d_file));

		int recv = 0;
		int buffer_size = 1024;
		//FileInputStream inFile = new FileInputStream(src_filename);
		//FileOutputStream outFile = new FileOutputStream(d_file);
		byte[] fileInBytes = new byte[buffer_size];

		while ((recv = getData.read(fileInBytes, 0, buffer_size)) != -1)
		{
			IntoFile.write(fileInBytes, 0, recv);
		}

		//inFile.read(fileInBytes);
		IntoFile.flush();
		IntoFile.close();
		dataSocket.close();

		ServerMsg = readLine();
		if (!ServerMsg.startsWith("226 "))
			return false;
		System.out.println(ServerMsg);

		System.out.println("Download is completed");
		return true;
	}

	
	// upload filename to server and rename it as Sfilename
	public boolean uploadFile(String filename, String Sfilename)
	{
		try
		{
			File file;
			if (filename.startsWith("/"))
				file = new File(new File(".").getCanonicalFile(), filename);
			else
				file = new File(filename);


			if (!file.isFile())
			{
				System.out.println("It is not a file or does not exist");
				return false;
			}

			Address newPort = forFunc.PASV();				//enter passive mode

			if (newPort.Port == -1)
				return false;

			Socket dataSocket = new Socket(newPort.IP, newPort.Port);	//connect to the data socket of server;

			sendLine("STOR " + Sfilename);

			String ServerMsg = readLine();
			if (!ServerMsg.startsWith("150 "))
				return false;
			System.out.println(ServerMsg);

			//------------------------------ uploading file ------------------------------------------
			BufferedOutputStream outData = new BufferedOutputStream(dataSocket.getOutputStream());

			FileInputStream inFile = new FileInputStream(file.getAbsolutePath());
			int numOfBytes = 1024;
			byte[] fileInBytes = new byte[numOfBytes];
			
			while ( ( inFile.read(fileInBytes) != -1))
			{
				outData.write(fileInBytes, 0, numOfBytes);				
			}
			inFile.close();
			outData.flush();
			
			outData.close();


			//---------------------------------------------------------------------------------
			ServerMsg = readLine();
			System.out.println(ServerMsg);
			if (!ServerMsg.startsWith("226 "))
			{
				dataSocket.close();
				return false;
			}

			dataSocket.close();


			System.out.println("Upload is completed");
			return true;
		}
		catch (Exception e)
		{
			try
			{
				System.out.println("The file can't upload");
				String ServerMsg = readLine();
				System.out.println(ServerMsg);
				if (!ServerMsg.startsWith("226 "))
				{
					//dataSocket.close();
					//return false;
				}
			}
			catch(Exception r)
			{
			}
		}
		return false;
	}

	private static void sendLine(String line) throws IOException{
	    if (clientSocket != null) {
	      outToServer.write(line + "\r\n");
	      outToServer.flush();
	    }
	}

	private static String readLine() throws IOException{
		String line = inFromServer.readLine();
	    return line;
	}

	private static void printServerMsg() throws IOException
	{

		String temp = readLine();
		System.out.println(temp);
		while (inFromServer.ready())
		{

			System.out.println(temp);
			temp = readLine();
		}

	}
}












